package com.tubeonfire.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import java.util.logging.Logger;

import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;

import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyOpts;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Query;
import com.tubeonfire.entity.SpecialTube;
import com.tubeonfire.entity.Tube;
import com.tubeonfire.search.admin.TubeSearchEngine;

public class TubeModel {

	private static final Logger log = Logger.getLogger(TubeModel.class
			.getName());

	private static Objectify ofy;

	private static Cache cache = null;

	private static boolean isRegisted = false;

	private static ObjectifyOpts opts = null;

	private static String cacheSide = "frontEnd_";

	private static String cachePrefix = "tubeModel_";

	private static TreeMap<String, String> mapCacheKey = new TreeMap<String, String>();

	private int limit = 30;

	private int page = 1;

	private int totalResult = 0;

	private int totalPage = 1;

	private List<Tube> listResult = new ArrayList<Tube>();

	public int getTotalPage() {
		totalPage = totalResult / limit;
		if ((totalResult % limit) > 0) {
			totalPage += 1;
		}
		return totalPage;
	}

	public void setTotalPage(int totalPage) {
		this.totalPage = totalPage;
	}

	public int getLimit() {
		return limit;
	}

	public void setLimit(int limit) {
		this.limit = limit;
	}

	public int getPage() {
		return page;
	}

	public void setPage(int page) {
		this.page = page;
	}

	public int getTotalResult() {
		return totalResult;
	}

	public void setTotalResult(int totalResult) {
		this.totalResult = totalResult;
	}

	public List<Tube> getListResult() {
		return listResult;
	}

	public void setListResult(List<Tube> listResult) {
		this.listResult = listResult;
	}

	public static void init() {
		if (!isRegisted) {
			isRegisted = true;
			try {
				ObjectifyService.register(Tube.class);
			} catch (Exception e) {
				isRegisted = false;
			}
			try {
				cache = CacheManager.getInstance().getCacheFactory()
						.createCache(Collections.emptyMap());
			} catch (CacheException e) {
				isRegisted = false;
			}
			opts = new ObjectifyOpts().setSessionCache(true);
		}
		ofy = ObjectifyService.begin(opts);
	}

	public TubeModel() {
		init();
	}

	@SuppressWarnings("unchecked")
	public static void update(Tube obj) {
		init();
		if (cache != null) {
			String prefix = cachePrefix + "id_" + obj.getId();
			cache.put(prefix, obj);
		}
		ofy.put(obj);
		TubeSearchEngine.update(obj);
		clearModelCache();
		com.tubeonfire.model.admin.TubeModel.clearModelCache();
		com.tubeonfire.search.TubeSearchModel.clearModelCache();
	}

	@SuppressWarnings("unchecked")
	public static Tube getById(String id) {
		try {
			init();
			boolean cached = false;
			Tube obj = new Tube();
			String prefix = cachePrefix + "id_" + id;
			if (cache != null) {
				try {
					obj = (Tube) cache.get(prefix);
					if (obj != null)
						cached = true;
				} catch (Exception e) {
					cached = false;
				}
			}
			if (!cached) {
				try {
					obj = ofy.get(new Key<Tube>(Tube.class, id));
					cache.put(prefix, obj);
				} catch (Exception e) {
					obj = null;
				}
			}
			return obj;
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			return null;
		}

	}

	@SuppressWarnings("unchecked")
	public void prepareTubes() {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "tubes_" + page;
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get("tubesTotalResult");
				limit = (Integer) cache.get("tubesLimit");
			} else {
				int start = (page - 1) * limit;
				Query<Tube> q = ofy.query(Tube.class).order("-bumpPoint");
				totalResult = q.count();
				q = q.limit(limit).offset(start);
				for (Tube tub : q) {
					listResult.add(tub);
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put("tubesTotalResult", totalResult);
					cache.put("tubesLimit", limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareTubeByPlaylist(String playlistId) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "playlist_" + playlistId
					+ "_page_" + page;
			mapCacheKey.put(prefix, prefix);
			String totalResultPrefix = cachePrefix + "playlist_" + playlistId
					+ "TotalResult";
			String limitPrefix = cachePrefix + "playlist_" + playlistId
					+ "Limit";
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get(totalResultPrefix);
				limit = (Integer) cache.get(limitPrefix);
			} else {
				int start = (page - 1) * limit;
				Query<Tube> q;
				if (cache != null && cache.containsKey(totalResultPrefix)
						&& cache.containsKey(limitPrefix)) {
					q = ofy.query(Tube.class).filter("playlistId", playlistId)
							.order("-bumpPoint").limit(limit).offset(start);
					totalResult = (Integer) cache.get(totalResultPrefix);
					limit = (Integer) cache.get(limitPrefix);
				} else {
					q = ofy.query(Tube.class).filter("playlistId", playlistId)
							.order("-bumpPoint");
					totalResult = q.count();
					q = q.limit(limit).offset(start);
				}
				for (Tube tub : q) {
					listResult.add(tub);
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put(totalResultPrefix, totalResult);
					cache.put(limitPrefix, limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareTubeByChannel(String channelId) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "channelId_" + channelId
					+ "_page_" + page;
			mapCacheKey.put(prefix, prefix);
			String totalResultPrefix = cachePrefix + "channelId_" + channelId
					+ "TotalResult";
			String limitPrefix = cachePrefix + "channelId_" + channelId
					+ "Limit";
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
				totalResult = (Integer) cache.get(totalResultPrefix);
				limit = (Integer) cache.get(limitPrefix);
			} else {
				int start = (page - 1) * limit;
				Query<Tube> q;
				if (cache != null && cache.containsKey(totalResultPrefix)
						&& cache.containsKey(limitPrefix)) {
					q = ofy.query(Tube.class).filter("channelId", channelId)
							.order("-bumpPoint").limit(limit).offset(start);
					totalResult = (Integer) cache.get(totalResultPrefix);
					limit = (Integer) cache.get(limitPrefix);
				} else {
					q = ofy.query(Tube.class).filter("channelId", channelId)
							.order("-bumpPoint");
					totalResult = q.count();
					q = q.limit(limit).offset(start);
				}
				for (Tube tub : q) {
					listResult.add(tub);
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
					cache.put(totalResultPrefix, totalResult);
					cache.put(limitPrefix, limit);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareBanner() {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "slideshow";
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				SpecialTube spTube = SpecialTubeModel.getByType("slide", true);
				if (spTube != null) {
					for (String strId : spTube.getListTubeId()) {
						listResult.add(getById(strId));
					}
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareLatestTube() {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "latest";
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				int start = (page - 1) * limit;
				Query<Tube> q = ofy.query(Tube.class).order("-updated")
						.limit(limit).offset(start);
				for (Tube tub : q) {
					listResult.add(tub);
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareHomePageTubeByPlaylist(String playlistId) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "homepagePlaylist_"
					+ playlistId;
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				int start = (page - 1) * limit;
				Query<Tube> q = ofy.query(Tube.class)
						.filter("playlistId", playlistId).order("-bumpPoint");
				totalResult = q.count();
				q = q.limit(limit).offset(start);
				for (Tube tub : q) {
					listResult.add(tub);
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareHomePageTubeByChannel(String channelId) {
		try {
			listResult = new ArrayList<Tube>();
			String prefix = cacheSide + cachePrefix + "homepageChannel_"
					+ channelId;
			mapCacheKey.put(prefix, prefix);
			if (cache != null && cache.containsKey(prefix)) {
				listResult = (ArrayList<Tube>) cache.get(prefix);
			} else {
				int start = (page - 1) * limit;
				Query<Tube> q = ofy.query(Tube.class)
						.filter("channelId", channelId).order("-bumpPoint");
				totalResult = q.count();
				q = q.limit(limit).offset(start);
				for (Tube tub : q) {
					listResult.add(tub);
				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	@SuppressWarnings("unchecked")
	public void prepareRelateTube(Tube obj) {
		try {
			listResult = new ArrayList<Tube>();
			boolean cached = false;
			String prefix = cacheSide + cachePrefix + "relate_with_id_"
					+ obj.getId();
			mapCacheKey.put(prefix, prefix);
			String playlistKeyPoolPrefix = "";
			playlistKeyPoolPrefix = cachePrefix + "playlistIdPool_"
					+ obj.getPlaylistId();

			if (cache != null && cache.containsKey(prefix)) {
				try {
					listResult = (ArrayList<Tube>) cache.get(prefix);
					if (listResult != null && listResult.size() > 0)
						cached = true;
				} catch (Exception e) {
					cached = false;
				}
			}
			if (!cached) {
				// get pool id by playlist
				List<Key<Tube>> keyPool = new ArrayList<Key<Tube>>();
				boolean cachedKeyPool = false;
				if (cache != null) {
					try {
						keyPool = (ArrayList<Key<Tube>>) cache
								.get(playlistKeyPoolPrefix);
						if (keyPool != null && keyPool.size() > 0)
							cachedKeyPool = true;
					} catch (Exception e) {
						cachedKeyPool = false;
					}

				}
				if (!cachedKeyPool) {
					keyPool = ofy.query(Tube.class)
							.filter("playlistId", obj.getPlaylistId())
							.listKeys();
					cache.put(playlistKeyPoolPrefix, keyPool);
				}
				// get list id random from pool
				List<Key<Tube>> relateKey = new ArrayList<Key<Tube>>();
				if (keyPool.size() > 20) {
					Random randomGenerator = new Random();
					int index = randomGenerator
							.nextInt((int) (keyPool.size() / 2));
					relateKey = keyPool.subList(index, (index + 10));
				} else if (keyPool.size() < 5) {
					relateKey = ofy.query(Tube.class).limit(10).listKeys();
				} else {
					relateKey = keyPool;
				}

				// get tube by list key
				Map<Key<Tube>, Tube> mapTube = ofy.get(relateKey);
				for (Tube tub : mapTube.values()) {
					if (!tub.getId().equals(obj.getId())) {
						listResult.add(tub);
					}

				}
				if (listResult.size() > 0) {
					cache.put(prefix, listResult);
				}
			}
		} catch (Exception e) {
			log.warning(e.toString());
			e.printStackTrace();
			listResult = new ArrayList<Tube>();
		}
	}

	public static void clearModelCache() {
		init();
		try {
			if (mapCacheKey != null && mapCacheKey.size() > 0) {
				for (String key : mapCacheKey.keySet()) {
					cache.remove(key);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
